package com.hx.nio;

import jdk.management.resource.internal.inst.SocketChannelImplRMHooks;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @author Huathy
 * @date 2021-10-19 23:31
 * @description NIO的操作过于繁琐，于是有了netty
 * netty就是对于这一系列的操作进行了封装。
 */
public class NioServerDemo {
    int port = 8888;

    //准备两个东西：轮询器Selector；缓冲区Buffer
    private Selector selector;      //叫号器、大堂经理
    private ByteBuffer buffer = ByteBuffer.allocate(1024);  //缓冲区

    /**
     * 初始化
     *
     * @param port
     */
    public NioServerDemo(int port) {
        //初始化轮询器（大堂经理、准备营业）
        this.port = port;
        try {
            ServerSocketChannel server = ServerSocketChannel.open();
            //告诉地址，IP : PORT
            server.bind(new InetSocketAddress(this.port));
            // BIO升级NIO，NIO为兼容BIO，NIO模型默认采用阻塞通信模式
            server.configureBlocking(false);
            //轮询器准备就绪(大堂经理准备就绪，营业)
            selector = Selector.open();
            //注册，准备叫号
            server.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void listen() {
        System.out.println("server start listen on : " + this.port);
        try {
            while (true) {
                selector.select();
                Set<SelectionKey> keySet = selector.selectedKeys();
                Iterator<SelectionKey> keys = keySet.iterator();
                //不断的迭代轮询
                //同步体现在这里，因为每次只能获得一个key，每次只能处理一种状态
                while (keys.hasNext()){
                    SelectionKey key = keys.next();
                    keys.remove();;
                    //每一个key表示一种状态
                    porcess(key);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 具体业务处理方法
     * 每一次轮询就调用一次process方法。而每次调用只能做一件事
     * 在同一时间，只能做一件事。体现了同步（阻塞）。
     * @param key
     * @throws IOException
     */
    private void porcess(SelectionKey key) throws IOException {
        // 针对每一种状态做不同的响应
        //当数据准备就绪时，将状态修改为可读。
        if(key.isAcceptable()){
            ServerSocketChannel server = (ServerSocketChannel)key.channel();
            SocketChannel channel = server.accept();
            //这个方法踢下线了非阻塞，不管数据是否准备好了。
            //获取服务端的：ServerSocketChannel
            channel.configureBlocking(false);
            key = channel.register(selector,SelectionKey.OP_READ);
        }else if(key.isReadable()){
            SocketChannel channel = (SocketChannel) key.channel();
            int len = channel.read(buffer);
            if( len > 0 ){
                buffer.flip();
                String content  = new String(buffer.array(),0,len);
                System.out.println("读取内容：" + content);
                //在key上携带一个附件，一会再写出
                channel.register(selector,SelectionKey.OP_WRITE);
                key.attach(content);
            }
        }else if(key.isWritable()){
            SocketChannel channel = (SocketChannel) key.channel();
            String content = (String) key.attachment();
            channel.write(ByteBuffer.wrap( ( "输出"+content).getBytes() ) );
            channel.close();
        }
    }

    public static void main(String[] args) {
        new NioServerDemo(8888).listen();
    }

}
//00 19 37
